﻿using Mengsk.ProxyDelivery.HTTPHelpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Mengsk.ProxyDelivery.Dao.NETDao
{
    public class RestfulDao<E> : IDao<E> where E : new()
    {
        public string BaseURL { get; set; }

        public RestfulDao()
        {
        }

        public RestfulDao(string baseUrl)
        {
            this.BaseURL = baseUrl;
        }

        public Result<T> Do<T>(string url, Dictionary<string, string> param, string method) where T : new()
        {
            param["request_format"] = "xml";
            url = BaseURL + url;
            Result<T> ret = new Result<T>();
            int total = 0;
            int pageIndex = 0;
            do
            {
                param["pageIndex"] = pageIndex.ToString();
                param["pageSize"] = "50";

                string xml = method == "POST" ? HttpUtilEx.DoPostWithRetry(3, url, Encoding.UTF8, "", "", param) : HttpUtilEx.DoGetWithRetry(3, url, Encoding.UTF8, "", "", param);
                if (string.IsNullOrWhiteSpace(xml))
                {
                    throw new Exception("HTTP请求返回字符串为空");
                }

                var tmpRet = Result<T>.ParseXml(xml);
                if (tmpRet.Error.Equals("SUCCESS", StringComparison.OrdinalIgnoreCase) == false)
                {
                    throw new Exception(tmpRet.Error);
                }
                ret.TotalNumber = tmpRet.TotalNumber;
                total += tmpRet.Datas.Count;
                ret.Datas.AddRange(tmpRet.Datas);
                pageIndex++;
            } while (total < ret.TotalNumber);
            ret.TotalNumber = total;
            ret.Error = "SUCCESS";
            return ret;
        }

        public Result<T> DoPost<T>(string url, Dictionary<string, string> param) where T : new()
        {
            return Do<T>(url, param, "POST");
        }

        public Result<T> DoGet<T>(string url, Dictionary<string, string> param) where T : new()
        {
            return Do<T>(url, param, "GET");
        }

        public Result<T> DoPost<T>(Request request, string url = null, IDictionary<string, string> headers = null) where T : new()
        {
            string xml = MSHttpRestful.DoPostWithRetry<string>(() => MSHttpRestful.PostStringBodyReturnString("", request.ToString(), Encoding.UTF8, headers));
            return Result<T>.ParseXml(xml);
        }

        public E GetById(object id)
        {
            Request req = new Request { Api = "getById" };
            req.Parameters.Add(new Parameter { Name = "id", Value = id.ToString() });
            Result<E> ret = this.DoPost<E>(req);
            if (ret.Datas.Count > 0)
            {
                return ret.Datas[0];
            }
            return default(E);
        }

        public E GetByField(string field, object value)
        {
            Request req = new Request { Api = "getByField" };
            req.Parameters.Add(new Parameter { Name = "field", Value = field });
            req.Parameters.Add(new Parameter { Name = "value", Value = value });
            Result<E> ret = this.DoPost<E>(req);
            if (ret.Datas.Count > 0)
            {
                return ret.Datas[0];
            }
            return default(E);
        }

        public IList<E> GetAllByField(string field, object value)
        {
            Request req = new Request { Api = "getAllByField" };
            req.Parameters.Add(new Parameter { Name = "field", Value = field });
            req.Parameters.Add(new Parameter { Name = "value", Value = value });
            Result<E> ret = this.DoPost<E>(req);

            return ret.Datas;
        }

        public E GetByFieldLike(string field, string value)
        {
            Request req = new Request { Api = "getByFieldLike" };
            req.Parameters.Add(new Parameter { Name = "field", Value = field });
            req.Parameters.Add(new Parameter { Name = "value", Value = value });
            Result<E> ret = this.DoPost<E>(req);
            if (ret.Datas.Count > 0)
            {
                return ret.Datas[0];
            }
            return default(E);
        }

        public IList<E> GetAllByFieldLike(string field, string value)
        {
            Request req = new Request { Api = "getAllByFieldLike" };
            req.Parameters.Add(new Parameter { Name = "field", Value = field });
            req.Parameters.Add(new Parameter { Name = "value", Value = value });
            Result<E> ret = this.DoPost<E>(req);

            return ret.Datas;
        }

        public int GetCount(IDictionary<string, object> param)
        {
            Request req = new Request { Api = "getCountByMap" };
            req.Parameters.Add(new Parameter { Name = "param", Value = param });
            Result<int> ret = this.DoPost<int>(req);
            return (int)ret.Datas[0];
        }

        public int GetCount(string query, params object[] objects)
        {
            Request req = new Request { Api = "getCountByMap" };
            req.Parameters.Add(new Parameter { Name = "query", Value = query });
            req.Parameters.Add(new Parameter { Name = "objects", Value = objects });
            Result<int> ret = this.DoPost<int>(req);
            return (int)ret.Datas[0];
        }

        public IList<E> Get(IDictionary<string, object> param, int pageIndex, int pageSize)
        {
            Request req = new Request { Api = "getPageByMap" };
            req.Parameters.Add(new Parameter { Name = "param", Value = param });
            req.Parameters.Add(new Parameter { Name = "pageIndex", Value = pageIndex });
            req.Parameters.Add(new Parameter { Name = "pageSize", Value = pageSize });
            Result<E> ret = this.DoPost<E>(req);

            return ret.Datas;
        }

        public DateTime GetDBMinTime()
        {
            Request req = new Request { Api = "getDBMinDate" };

            Result<DateTime> ret = this.DoPost<DateTime>(req);

            if (ret.Datas.Count > 0)
            {
                return ret.Datas[0];
            }
            throw new Exception("无法获取服务器数据库所使用的最小时间");
        }

        public bool IsDBMinTime(DateTime time)
        {
            Request req = new Request { Api = "isDBMinDate" };
            req.Parameters.Add(new Parameter { Name = "date", Value = time });

            Result<bool> ret = this.DoPost<bool>(req);

            if (ret.Datas.Count > 0)
            {
                return ret.Datas[0];
            }
            throw new Exception("无法检查时间是否是服务器数据库的最小值");
        }
    }
}
